#region

using System;
using System.Drawing;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using Microsoft.Win32;
using net.windward.utils;

#endregion

namespace Kailua.net.windward.utils
{
	///<summary>
	/// All the stuff Microsoft forget to put in .NET.
	///</summary>
	public class NativeMethods
	{
		#region Delegates

		/// <summary>
		/// The hook delegate for hooking in to the message pump.
		/// </summary>
		/// <param name="nCode">The Windows message.</param>
		/// <param name="wParam">The message WORD.</param>
		/// <param name="lParam">The message DWORD.</param>
		/// <returns>The return value for a SendMessage.</returns>
		public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

		#endregion

		#region IconSize enum

		/// <summary>The icon size</summary>
		public enum IconSize : uint
		{
			/// <summary>32x32</summary>
			Large = 0x0,
			/// <summary>16x16</summary>
			Small = 0x1
		}

		#endregion

		private const Int32 ID_NO = 7;
		private const Int32 ID_OK = 1;
		private const Int32 ID_YES = 6;

		private const int LOGPIXELSX = 88;
		private const UInt32 MB_ICONERROR = 0x00000010;
		private const UInt32 MB_ICONINFORMATION = 0x00000040;
		private const UInt32 MB_ICONWARNING = 0x00000030;
		private const UInt32 MB_OK = 0x00000000;
		private const UInt32 MB_SETFOREGROUND = 0x00010000;
		private const UInt32 MB_SYSTEMMODAL = 0x00001000;
		private const UInt32 MB_TOPMOST = 0x00040000;
		private const UInt32 MB_YESNO = 0x00000004;

		/// <summary>
		/// Hook procedure for mouse events.
		/// </summary>
		public const int WH_MOUSE = 7;

		/// <summary>Hook procedure for global and low-level mouse events.</summary>
		public const int WH_MOUSE_LL = 14;
		/// <summary>Hook procedure for left mouse button down.</summary>
		public const int WM_LBUTTONDOWN = 0x0201;
		/// <summary>Hook procedure for left mouse button up.</summary>
		public const int WM_LBUTTONUP = 0x0202;
		/// <summary>Hook procedure for mouse moved.</summary>
		public const int WM_MOUSEMOVE = 0x0200;
		/// <summary>Hook procedure for mouse wheel.</summary>
		public const int WM_MOUSEWHEEL = 0x020A;
		/// <summary>Hook procedure for right mouse button down.</summary>
		public const int WM_RBUTTONDOWN = 0x0204;
		/// <summary>Hook procedure for right mouse button up.</summary>
		public const int WM_RBUTTONUP = 0x0205;
		private const int WS_SHOWNORMAL = 1;
		private static int _dpi = -1;

		///<summary>
		/// The Dots Per Inch for the video display.
		///</summary>
		public static int DPI
		{
			get
			{
				if (_dpi != -1)
					return _dpi;

				_dpi = 96;
				try
				{
					IntPtr hdc = CreateDC("DISPLAY", null, null, IntPtr.Zero);
					if (hdc != IntPtr.Zero)
					{
						_dpi = GetDeviceCaps(hdc, LOGPIXELSX);
						if (_dpi == 0)
							_dpi = 96;
						DeleteDC(hdc);
					}
				}
				catch (Exception)
				{
					Trap.trap();
				}

				return _dpi;
			}
		}

		[DllImport("gdi32.dll", EntryPoint = "CreateDC", CharSet = CharSet.Auto, SetLastError = true)]
		private static extern IntPtr CreateDC(string lpszDriver, string lpszDeviceName, string lpszOutput, IntPtr devMode);

		[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
		private static extern bool DeleteDC(IntPtr hdc);

		[DllImport("gdi32.dll", SetLastError = true)]
		private static extern Int32 GetDeviceCaps(IntPtr hdc, Int32 capindex);

		[DllImport("user32.dll")]
		[return: MarshalAs(UnmanagedType.Bool)]
		private static extern bool SetForegroundWindow(IntPtr hWnd);

		[DllImport("User32.dll", EntryPoint = "ShowWindowAsync")]
		private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);

		[DllImport("wininet.dll", CharSet = CharSet.None)]
		[return: MarshalAs(UnmanagedType.Bool)]
		private static extern bool InternetGetConnectedState(out uint lpdwFlags, uint dwReserved);

		[DllImport("advapi32.dll", SetLastError = true)]
		private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

		[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
		private extern static bool CloseHandle(IntPtr handle);
		const int LOGON32_PROVIDER_DEFAULT = 0;
		const int LOGON32_LOGON_NEW_CREDENTIALS = 9;

		/// <summary>
		/// The MessageBox function creates, displays, and operates a message box. 
		/// </summary>
		/// <param name="hwnd">Handle to the owner window of the message box to be created. If this parameter is NULL, the message box has no owner window.</param>
		/// <param name="lpText">Pointer to a null-terminated string that contains the message to be displayed.</param>
		/// <param name="lpCaption">Pointer to a null-terminated string that contains the dialog box title.</param>
		/// <param name="wType">Specifies the contents and behavior of the dialog box.</param>
		/// <returns></returns>
		[DllImport("user32", EntryPoint = "MessageBox")]
		private static extern int MessageBoxA(IntPtr hwnd, string lpText, string lpCaption, UInt32 wType);

		/// <summary>
		/// Set a Window LONG value.
		/// </summary>
		/// <param name="hWnd">hwnd of the window.</param>
		/// <param name="nIndex">key to property.</param>
		/// <param name="dwNewLong">value to set.</param>
		/// <returns>ignored.</returns>
		[DllImport("user32.dll")]
		private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);

		/// <summary>
		/// Set the parent window of a form.
		/// </summary>
		/// <param name="hWndChild">hwnd of the form.</param>
		/// <param name="hWndNewParent">hwnd of it's new parent.</param>
		/// <returns></returns>
		[DllImport("user32.dll", SetLastError = true)]
		private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

		[DllImport("Gdi32.dll")]
		private static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

		[DllImport("Gdi32.dll", CharSet = CharSet.Unicode)]
		private static extern int GetGlyphIndices(IntPtr hdc, [MarshalAs(UnmanagedType.LPWStr)] string lpstr, int c,
		                                          Int16[] pgi, int fl);

		/// <summary>
		/// The GetWindowRect function retrieves the dimensions of the bounding rectangle of the specified window. The dimensions 
		/// are given in screen coordinates that are relative to the upper-left corner of the screen.
		/// </summary>
		/// <param name="hWnd">Handle to the window.</param>
		/// <param name="lpRect">Pointer to a structure that receives the screen coordinates of the upper-left and lower-right corners of the window.</param>
		/// <returns>If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.</returns>
		[DllImport("user32.dll")]
		[return: MarshalAs(UnmanagedType.Bool)]
		private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

		/// <summary>
		/// Gets the position of the mouse for the presently processed message.
		/// </summary>
		/// <returns>The mouse position.</returns>
		[DllImport("user32.dll", EntryPoint = "GetMessagePos")]
		private static extern int GetMessagePos();

		[DllImport("Shell32", CharSet = CharSet.Auto)]
		private static extern int ExtractIconEx([MarshalAs(UnmanagedType.LPTStr)] string lpszFile, int nIconIndex,
		                                        IntPtr[] phIconLarge, IntPtr[] phIconSmall, int nIcons); //how many to get

		[DllImport("winmm.dll", EntryPoint = "mciSendStringA")]
		private static extern int mciSendString(string lpstrCommand, StringBuilder lpstrReturnString, int uReturnLength,
		                                        IntPtr hwndCallback);

		[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
		private static extern int GetShortPathName([MarshalAs(UnmanagedType.LPTStr)] string path,
		                                           [MarshalAs(UnmanagedType.LPTStr)] StringBuilder shortPath,
		                                           int shortPathLength);

		///<summary>
		/// Return the 8.3 form of the filename. The file must exist.
		///</summary>
		///<param name="longFilename">The long version of the filename.</param>
		///<returns>The short version of the filename.</returns>
		public static string GetShortPathName(string longFilename)
		{
			StringBuilder shortName = new StringBuilder(256);
			GetShortPathName(longFilename, shortName, shortName.Capacity);
			return shortName.ToString();
		}

		/// <summary>
		/// Play the passed in MIDI file
		/// </summary>
		/// <param name="midiFile">The name of the file.</param>
		/// <returns>true if started.</returns>
		public static bool PlayMidiFile(string midiFile)
		{
			if (File.Exists(midiFile))
			{
				midiFile = Path.GetFullPath(midiFile);
				midiFile = GetShortPathName(midiFile);
				// mciSendString("stop MediaFile", null, 0, 0);
				// mciSendString("close MediaFile", null, 0, 0);
				int rtn = mciSendString("Open \"" + midiFile + "\" type mpegvideo alias MediaFile", null, 0, IntPtr.Zero);
				if (rtn == 0)
					rtn = mciSendString("Play MediaFile", null, 0, IntPtr.Zero);
				return (rtn == 0);
			}
			return false;
		}

		/// <summary>
		/// Stop playing all MIDI files
		/// </summary>
		/// <returns>true if successful.</returns>
		public static bool StopMidiFile()
		{
			mciSendString("stop MediaFile", null, 0, IntPtr.Zero);
			int rtn = mciSendString("close MediaFile", null, 0, IntPtr.Zero);
			return (rtn == 0);
		}

		/// <summary>
		/// Set the window as the foreground window
		/// </summary>
		/// <param name="hWnd">The handle of the window.</param>
		public static void SetWindowAsForeground(IntPtr hWnd)
		{
			SetForegroundWindow(hWnd);
		}

		/// <summary>
		/// Set the window's state. Always set to normal.
		/// </summary>
		/// <param name="hWnd">The handle of the window.</param>
		public static void SetWindowState(IntPtr hWnd)
		{
			ShowWindowAsync(hWnd, WS_SHOWNORMAL);
		}

		/// <summary>
		/// Returns tru if the passed in string can be displayed using the passed in fontname. It checks the font to 
		/// see if it has glyphs for all the chars in the string.
		/// </summary>
		/// <param name="fontName">The name of the font to check.</param>
		/// <param name="text">The text to check for glyphs of.</param>
		/// <returns></returns>
		public static bool CanDisplayString(string fontName, string text)
		{
			try
			{
				IntPtr hdc = CreateDC("DISPLAY", null, null, IntPtr.Zero);
				if (hdc != IntPtr.Zero)
				{
					using (Font font = new Font(new FontFamily(fontName), 12, FontStyle.Regular, GraphicsUnit.Point))
					{
						SelectObject(hdc, font.ToHfont());
						int count = text.Length;
						Int16[] rtcode = new Int16[count];
						GetGlyphIndices(hdc, text, count, rtcode, 0xffff);
						DeleteDC(hdc);

						foreach (Int16 code in rtcode)
							if (code == 0)
								return false;
					}
				}
			}
			catch (Exception)
			{
				// nada - return true
				Trap.trap();
			}
			return true;
		}

		/// <summary>
		/// Set the owner of our form. The owner can be a non .NET window.
		/// </summary>
		/// <param name="child">The form who's owner will be set.</param>
		/// <param name="hwnd">The window that will be the owner's parent.</param>
		public static void SetFormOwner(Form child, IntPtr hwnd)
		{
			SetWindowLong(child.Handle, -8, (int) hwnd);
		}

		/// <summary>
		/// Set the parent of our form. The parent can be a non .NET window.
		/// </summary>
		/// <param name="child">The form who's parent will be set.</param>
		/// <param name="hwnd">The window that will be the form's parent.</param>
		public static void SetFormParent(Form child, IntPtr hwnd)
		{
			SetParent(child.Handle, hwnd);
		}

		/// <summary>
		/// Gets the position of the mouse for the presently processed message.
		/// </summary>
		/// <returns>The mouse position.</returns>
		public static Point GetMessageMousePos()
		{
			return new Point(GetMessagePos());
		}

		/// <summary>
		/// Get the location of this window on the screen.
		/// </summary>
		/// <param name="hwnd">The hWnd of the window.</param>
		/// <returns>The window's location. Returns Rectangle(0, 0, 0, 0) if there is an error.</returns>
		public static Rectangle GetNativeWindowRectangle(IWin32Window hwnd)
		{
			RECT rect;
			if (!GetWindowRect(hwnd.Handle, out rect))
			{
				Trap.trap();
				return new Rectangle();
			}
			return new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
		}

		/// <summary>
		/// Put up a MessageBox that is topmost.
		/// </summary>
		/// <param name="hwnd">Parent window.</param>
		/// <param name="message">Message.</param>
		/// <param name="caption">Captions.</param>
		/// <param name="buttons">Only OK and YESNO supported.</param>
		/// <param name="icon">Only INFO, WARNING, and ERROR supported.</param>
		/// <returns>The button pressed.</returns>
		public static DialogResult MessageBoxTopMost(IWin32Window hwnd, string message, string caption,
		                                             MessageBoxButtons buttons, MessageBoxIcon icon)
		{
			UInt32 mb = MB_TOPMOST | MB_SYSTEMMODAL | MB_SETFOREGROUND;
			switch (buttons)
			{
				case MessageBoxButtons.OK:
					mb |= MB_OK;
					break;
				case MessageBoxButtons.YesNo:
					mb |= MB_YESNO;
					break;
			}
			switch (icon)
			{
				case MessageBoxIcon.Information:
					mb |= MB_ICONINFORMATION;
					break;
				case MessageBoxIcon.Warning:
					mb |= MB_ICONWARNING;
					break;
				case MessageBoxIcon.Error:
					mb |= MB_ICONERROR;
					break;
			}

			int rtn = MessageBoxA(hwnd == null ? (IntPtr) 0 : hwnd.Handle, message, caption, mb);

			switch (rtn)
			{
				case ID_OK:
					return DialogResult.OK;
				case ID_YES:
					return DialogResult.Yes;
				case ID_NO:
					return DialogResult.No;
			}
			return DialogResult.OK;
		}

		/// <summary>
		/// This will look in the registry for the icon.
		/// </summary>
		/// <param name="Extension">The extension name - should have the period.</param>
		/// <param name="Size">WHat size icon.</param>
		/// <returns>The icon or null if not found.</returns>
		public static Icon IconFromExtension(string Extension, IconSize Size)
		{
			try
			{
				//add '.' if nessesry
				if (Extension[0] != '.')
					Extension = '.' + Extension;

				//opens the registry for the wanted key.
				using (RegistryKey ExtensionKey = Registry.ClassesRoot.OpenSubKey(Extension))
				{
					if (ExtensionKey != null)
					{
						ExtensionKey.GetValueNames();
						using (RegistryKey ApplicationKey = Registry.ClassesRoot.OpenSubKey(ExtensionKey.GetValue("").ToString()))
						{
							if (ApplicationKey != null)
							{
								//gets the name of the file that have the icon.
								using (RegistryKey key = ApplicationKey.OpenSubKey("DefaultIcon"))
								{
									if (key != null)
									{
										string IconLocation = key.GetValue("").ToString();
										string[] IconPath = IconLocation.Split(',');

										if (IconPath[1] == null)
											IconPath[1] = "0";
										IntPtr[] Large = new IntPtr[1], Small = new IntPtr[1];

										//extracts the icon from the file.
										ExtractIconEx(IconPath[0], Convert.ToInt16(IconPath[1]), Large, Small, 1);
										return Size == IconSize.Large ? Icon.FromHandle(Large[0]) : Icon.FromHandle(Small[0]);
									}
								}
							}
						}
					}
				}
				return null;
			}
			catch (Exception)
			{
				return null;
			}
		}

		/// <summary>
		/// Determines if can access a webite. First checks connection state (that is fast). Requests the HEAD to verify.
		/// </summary>
		/// <param name="uri">The uri to look for.</param>
		/// <param name="timeout">The timeout in milliseconds. Reccomend 10000</param>
		/// <returns></returns>
		public static bool CanLikelyAccessWebSite(Uri uri, int timeout)
		{
			uint unused;
			if (!InternetGetConnectedState(out unused, 0U))
				return false;

			try
			{
				HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
				request.Method = "HEAD";
				request.Timeout = timeout;
				using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
				{
					return response != null;
				}
			}
			catch (WebException)
			{
				return false;
			}
		}

		/// <summary>
		/// Open an XML file with login credentials.
		/// </summary>
		/// <param name="filename">The file to open.</param>
		/// <param name="xmlSettings">The XML settings for the open.</param>
		/// <param name="domain">The credentials domain (can be "." for workgroup).</param>
		/// <param name="username">The credentials username.</param>
		/// <param name="password">The credentials password.</param>
		/// <returns>The XML file.</returns>
		public static XmlReader OpenAsUser(string filename, XmlReaderSettings xmlSettings, string domain, string username, string password)
		{
			IntPtr tokenHandle = new IntPtr(0);
			bool returnValue = LogonUser(username, domain, password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref tokenHandle);
			if (returnValue == false)
				return null;
			try
			{
				WindowsIdentity identity = new WindowsIdentity(tokenHandle);
				using (WindowsImpersonationContext impersonationContext = identity.Impersonate())
				{
					try
					{
						return XmlReader.Create(filename, xmlSettings);
					}
					finally
					{
						impersonationContext.Undo();
					}
				}
			}
			finally
			{
				CloseHandle(tokenHandle);
			}
		}

		/// <summary>
		/// Read a file with login credentials.
		/// </summary>
		/// <param name="filename">The file to open.</param>
		/// <param name="domain">The credentials domain (can be "." for workgroup).</param>
		/// <param name="username">The credentials username.</param>
		/// <param name="password">The credentials password.</param>
		/// <returns>The file contents.</returns>
		public static byte [] OpenAsUser(string filename, string domain, string username, string password)
		{

			if ((domain == null) && ((username.IndexOf('/') != -1) || (username.IndexOf('\\') != -1)))
			{
				int pos = username.IndexOf('/');
				if (pos == -1)
					pos = username.IndexOf('\\');
				domain = username.Substring(0, pos);
				username = username.Substring(pos + 1);
			}

			IntPtr tokenHandle = new IntPtr(0);
			bool returnValue = LogonUser(username, domain, password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref tokenHandle);
			if (returnValue == false)
				return null;
			try
			{
				WindowsIdentity identity = new WindowsIdentity(tokenHandle);
				using (WindowsImpersonationContext impersonationContext = identity.Impersonate())
				{
					try
					{
						Trap.trap(domain == ".");
						return File.ReadAllBytes(filename);
					}
					finally
					{
						impersonationContext.Undo();
					}
				}
			}
			finally
			{
				CloseHandle(tokenHandle);
			}
		}

		#region Nested type: RECT

		[StructLayout(LayoutKind.Sequential)]
		private struct RECT
		{
			public readonly int Left;
			public readonly int Top;
			public readonly int Right;
			public readonly int Bottom;
		} ;

		#endregion
	}
}